<?php
/**
 * Core Bootstrap
 *
 * This file contains all common system functions and View and Controller classes.
 *
 * @package        MicroMVC
 * @author         David Pennington
 * @copyright      (c) 2011 MicroMVC Framework
 * @license        http://micromvc.com/license
 ********************************** 80 Columns *********************************
 */


/**
 * Attach (or remove) multiple callbacks to an event and trigger those callbacks when that event is called.
 *
 * @param string $event    name
 * @param mixed  $value    the optional value to pass to each callback
 * @param mixed  $callback the method or function to call - FALSE to remove all callbacks for event
 */
function event($event, $value = NULL, $callback = NULL)
{
    static $events;

    // Adding or removing a callback?
    if ($callback !== NULL)
    {
        if ($callback)
        {
            $events[$event][] = $callback;
        }
        else
        {
            unset($events[$event]);
        }
    }
    elseif (isset($events[$event])) // Fire a callback
    {
        foreach ($events[$event] as $function)
        {
            $value = call_user_func($function, $value);
        }
        return $value;
    }
}


/**
 * Fetch a config value from a module configuration file
 *
 * @param string  $file  name of the config
 * @param boolean $clear to clear the config object
 * @return object
 */
function config($key = false, $default = null)
{
    return \Core\Config::get($key, $default);
}

/**
 * 获取Option
 *
 * @param bool $key
 * @param null $default
 * @return null
 */
function option($key = false, $default = null)
{
    $option = \Core\Cache::instance('config')->get('option');
    return array_key_exists($key, $option) ? $option[$key] : $default;
}


/**
 * Automatically load the given class
 *
 * @param string $class name
 */
function autoload($className)
{
    $className = ltrim($className, '\\');
    $fileName = '';
    $namespace = '';

    if ($lastNsPos = strripos($className, '\\'))
    {
        $namespace = substr($className, 0, $lastNsPos);
        $className = substr($className, $lastNsPos + 1);
        $fileName = str_replace('\\', DS, $namespace) . DS;
    }

    $fileName .= str_replace('_', DS, $className) . '.php';
    $fileName = SP . 'class/' . strtolower($fileName);

    if (is_file($fileName)) require $fileName;
}

/**
 * Return an HTML safe dump of the given variable(s) surrounded by "pre" tags.
 * You can pass any number of variables (of any type) to this function.
 *
 * @param mixed
 * @return string
 */
function dump()
{
    $string = '';
    foreach (func_get_args() as $value)
    {
        $string .= '<pre>' . h($value === NULL ? 'NULL' : (is_scalar($value) ? $value : print_r($value, TRUE))) . "</pre>\n";
    }
    return $string;
}


/**
 * Safely fetch a $_POST value, defaulting to the value provided if the key is
 * not found.
 *
 * @param string  $key     name
 * @param mixed   $default value if key is not found
 * @param boolean $string  TRUE to require string type
 * @return mixed
 */
function post($key, $default = NULL, $string = FALSE)
{
    if (isset($_POST[$key]))
    {
        return $string ? str($_POST[$key], $default) : $_POST[$key];
    }
    return $default;
}


/**
 * Safely fetch a $_GET value, defaulting to the value provided if the key is
 * not found.
 *
 * @param string  $key     name
 * @param mixed   $default value if key is not found
 * @param boolean $string  TRUE to require string type
 * @return mixed
 */
function get($key, $default = NULL, $string = FALSE)
{
    if (isset($_GET[$key]))
    {
        return $string ? str($_GET[$key], $default) : $_GET[$key];
    }
    return $default;
}


/**
 * Safely fetch a $_SESSION value, defaulting to the value provided if the key is
 * not found.
 *
 * @param string $k the post key
 * @param mixed  $d the default value if key is not found
 * @return mixed
 */
function session($k, $d = NULL)
{
    return isset($_SESSION[$k]) ? $_SESSION[$k] : $d;
}


/**
 * Create a random 32 character MD5 token
 *
 * @return string
 */
function token()
{
    return md5(str_shuffle(chr(mt_rand(32, 126)) . uniqid() . microtime(TRUE)));
}


/**
 * Write to the application log file using error_log
 *
 * @param string $message to save
 * @return bool
 */
function log_message($message)
{
    $path = SP . 'storage/log/' . date('Y-m-d') . '.log';

    // Append date and IP to log message
    return error_log(date('H:i:s ') . getenv('REMOTE_ADDR') . " $message\n", 3, $path);
}


/**
 * Send a HTTP header redirect using "location" or "refresh".
 *
 * @param string $url    the URL string
 * @param int    $c      the HTTP status code
 * @param string $method either location or redirect
 */
function redirect($url = NULL, $code = 302, $method = 'location')
{
    if (strpos($url, '://') === FALSE)
    {
        $url = site_url($url);
    }

    //print dump($url);

    header($method == 'refresh' ? "Refresh:0;url = $url" : "Location: $url", TRUE, $code);
    if ($method == 'location') exit;
}


/*
 * Return the full URL to a path on this site or another.
 *
 * @param string $uri may contain another sites TLD
 * @return string
 *
function site_url($uri = NULL)
{
	return (strpos($uri, '://') === FALSE ? \Core\URL::get() : '') . ltrim($uri, '/');
}
*/

/**
 * Return the full URL to a location on this site
 *
 * @param string $path   to use or FALSE for current path
 * @param array  $params to append to URL
 * @return string
 */
function site_url($path = NULL, array $params = NULL)
{
    // In PHP 5.4, http_build_query will support RFC 3986
    return DOMAIN . ($path ? '/' . trim($path, '/') : PATH)
        . ($params ? '?' . str_replace('+', '%20', http_build_query($params, TRUE, '&')) : '');
}


/**
 * Return the current URL with path and query params
 *
 * @return string
 *
function current_url()
{
return DOMAIN . getenv('REQUEST_URI');
}
 */

/**
 * Convert a string from one encoding to another encoding
 * and remove invalid bytes sequences.
 *
 * @param string $string to convert
 * @param string $to     encoding you want the string in
 * @param string $from   encoding that string is in
 * @return string
 */
function encode($string, $to = 'UTF-8', $from = 'UTF-8')
{
    // ASCII is already valid UTF-8
    if ($to == 'UTF-8' AND is_ascii($string))
    {
        return $string;
    }

    // Convert the string
    return @iconv($from, $to . '//TRANSLIT//IGNORE', $string);
}


/**
 * Tests whether a string contains only 7bit ASCII characters.
 *
 * @param string $string to check
 * @return bool
 */
function is_ascii($string)
{
    return !preg_match('/[^\x00-\x7F]/S', $string);
}


/**
 * Encode a string so it is safe to pass through the URL
 *
 * @param string $string to encode
 * @return string
 */
function base64_url_encode($string = NULL)
{
    return strtr(base64_encode($string), '+/=', '-_~');
}


/**
 * Decode a string passed through the URL
 *
 * @param string $string to decode
 * @return string
 */
function base64_url_decode($string = NULL)
{
    return base64_decode(strtr($string, '-_~', '+/='));
}


/**
 * Convert special characters to HTML safe entities.
 *
 * @param string $string to encode
 * @return string
 */
function h($string)
{
    return htmlspecialchars($string, ENT_QUOTES, 'utf-8');
}


/**
 * Filter a valid UTF-8 string so that it contains only words, numbers,
 * dashes, underscores, periods, and spaces - all of which are safe
 * characters to use in file names, URI, XML, JSON, and (X)HTML.
 *
 * @param string $string to clean
 * @param bool   $spaces TRUE to allow spaces
 * @return string
 */
function sanitize($string, $spaces = TRUE)
{
    $search = array(
        '/[^\w\-\. ]+/u', // Remove non safe characters
        '/\s\s+/', // Remove extra whitespace
        '/\.\.+/', '/--+/', '/__+/' // Remove duplicate symbols
    );

    $string = preg_replace($search, array(' ', ' ', '.', '-', '_'), $string);

    if (!$spaces)
    {
        $string = preg_replace('/--+/', '-', str_replace(' ', '-', $string));
    }

    return trim($string, '-._ ');
}


/**
 * Create a SEO friendly URL string from a valid UTF-8 string.
 *
 * @param string $string to filter
 * @return string
 */
function sanitize_url($string)
{
    return urlencode(mb_strtolower(sanitize($string, FALSE)));
}


/**
 * Filter a valid UTF-8 string to be file name safe.
 *
 * @param string $string to filter
 * @return string
 */
function sanitize_filename($string)
{
    return sanitize($string, FALSE);
}


/**
 * Return a SQLite/MySQL/PostgreSQL datetime string
 *
 * @param int $timestamp
 */
function sql_date($timestamp = NULL)
{
    return date('Y-m-d H:i:s', $timestamp ? : time());
}


/**
 * Make a request to the given URL using cURL.
 *
 * @param string $url     to request
 * @param array  $options for cURL object
 * @return object
 */
function curl_request($url, array $options = NULL)
{
    $ch = curl_init($url);

    $defaults = array(
        CURLOPT_HEADER         => 0,
        CURLOPT_RETURNTRANSFER => 1,
        CURLOPT_TIMEOUT        => 5,
    );

    // Connection options override defaults if given
    curl_setopt_array($ch, (array)$options + $defaults);

    // Create a response object
    $object = new stdClass;

    // Get additional request info
    $object->response = curl_exec($ch);
    $object->error_code = curl_errno($ch);
    $object->error = curl_error($ch);
    $object->info = curl_getinfo($ch);

    curl_close($ch);

    return $object;
}


/**
 * Create a RecursiveDirectoryIterator object
 *
 * @param string  $dir       the directory to load
 * @param boolean $recursive to include subfolders
 * @return object
 */
function directory($dir, $recursive = TRUE)
{
    $i = new \RecursiveDirectoryIterator($dir);

    if (!$recursive) return $i;

    return new \RecursiveIteratorIterator($i, \RecursiveIteratorIterator::SELF_FIRST);
}


/**
 * Make sure that a directory exists and is writable by the current PHP process.
 *
 * @param string $dir   the directory to load
 * @param string $chmod value as octal
 * @return boolean
 */
function directory_is_writable($dir, $chmod = 0755)
{
    // If it doesn't exist, and can't be made
    if (!is_dir($dir) AND !mkdir($dir, $chmod, TRUE)) return FALSE;

    // If it isn't writable, and can't be made writable
    if (!is_writable($dir) AND !chmod($dir, $chmod)) return FALSE;

    return TRUE;
}


/**
 * Convert any given variable into a SimpleXML object
 *
 * @param mixed  $object  variable object to convert
 * @param string $root    root element name
 * @param object $xml     xml object
 * @param string $unknown element name for numeric keys
 * @param string $doctype XML doctype
 */
function to_xml($object, $root = 'root', $unknown = 'item', $doctype = "<?xml version = '1.0' encoding = 'utf-8'?>")
{
    return \Core\XML::fromArray($object, $root, $unknown, $doctype);
}


/**
 * Return an IntlDateFormatter object using the current system locale
 *
 * @param string  $locale   string
 * @param integer $datetype IntlDateFormatter constant
 * @param integer $timetype IntlDateFormatter constant
 * @param string  $timezone Time zone ID, default is system default
 * @return IntlDateFormatter
 */
function __date($locale = NULL, $datetype = IntlDateFormatter::MEDIUM, $timetype = IntlDateFormatter::SHORT, $timezone = NULL)
{
    return new IntlDateFormatter($locale ? : setlocale(LC_ALL, 0), $datetype, $timetype, $timezone);
}

/**
 * Use I18n Object to parse
 */
if (!function_exists('__'))
{
    function __($string, array $values = NULL, $lang = 'en')
    {
        if ($lang !== \Core\I18n::$lang)
        {
            $string = \Core\I18n::get($string);
        }

        return empty($values) ? $string : strtr($string, $values);
    }
}

/**
 * Color output text for the CLI
 *
 * @param string $text       to color
 * @param string $color      of text
 * @param string $background color
 */
function colorize($text, $color, $bold = FALSE)
{
    // Standard CLI colors
    $colors = array_flip(array(30 => 'gray', 'red', 'green', 'yellow', 'blue', 'purple', 'cyan', 'white', 'black'));

    // Escape string with color information
    return "\033[" . ($bold ? '1' : '0') . ';' . $colors[$color] . "m$text\033[0m";
}

// End
